package com.example.k8s.hello.service.common.http;

import cn.dev33.satoken.stp.StpUtil;
import com.example.k8s.hello.common.annotation.MyHttpExchange;
import com.example.k8s.hello.common.utils.HttpContextUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.core5.util.TimeValue;
import org.reflections.Reflections;
import org.reflections.scanners.Scanners;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.env.Environment;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.support.RestClientAdapter;
import org.springframework.web.service.invoker.HttpServiceProxyFactory;

import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.Set;

/**
 * 扫描HttpExchange注册到spring容器，并支持占位符${}替换
 */
@Component
public class HttpInterfaceInit implements BeanFactoryPostProcessor {

    private Environment environment;

    /**
     * springboot bean注册顺序：
     * 1.ApplicationContextInitializer执行，
     * 2.beanDefinition创建,
     * 3.BeanFactoryPostProcessor执行,
     * 4.bean创建并注册（依赖注入为暂时空），
     * 5.ApplicationContextAware执行
     * 6.@PostConstruct执行
     * 7.完成所有bean的依赖注入
     * @param beanFactory
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        environment = beanFactory.getBean(Environment.class);

        HttpServiceProxyFactory factory = getHttpServiceProxyFactory();

        Reflections reflections = new Reflections("com.example.k8s.hello.api", Scanners.TypesAnnotated);
        Set<Class<?>> annotatedClasses = reflections.getTypesAnnotatedWith(MyHttpExchange.class);

        for (Class<?> clazz : annotatedClasses) {

            // 如果容器中已有接口的实现类，则跳过
            if (beanFactory.getBeanNamesForType(clazz).length > 0) {
                return;
            }

            Object apiObj = factory.createClient(clazz);

            // 注册到容器
            beanFactory.registerSingleton(clazz.getSimpleName(), apiObj);
        }
    }

    public HttpServiceProxyFactory getHttpServiceProxyFactory() {
        RestClient restClient = getRestClient();

        // 代理restclient返回结果，实现对RestResponse结果的自动拆箱
        RestClientAdapterProxy restClientAdapterProxy = new RestClientAdapterProxy(RestClientAdapter.create(restClient));

        HttpServiceProxyFactory factory = HttpServiceProxyFactory
                .builderFor(restClientAdapterProxy)
                // 支持get请求中使用对象形式接受参数
                .customArgumentResolver(new ParamObjectResolver())
                .embeddedValueResolver(url -> {
                    // 解析占位符
                    String resolvedValue = environment.resolvePlaceholders(url);

                    return resolvedValue;
                }).build();
        return factory;
    }

    public RestClient getRestClient() {
        String tokenName = environment.getProperty("sa-token.token-name", StpUtil.getTokenName());

        RestClient restClient = RestClient.builder()
                .requestInitializer(request -> {
                    request.getHeaders().add(tokenName, HttpContextUtils.getRequest().getHeader(tokenName));
                })
                .requestFactory(getRequestFactory())
                .requestInterceptor(new RestClientInterceptor())
                .messageConverters(list -> {

                    // 使用配置中的日期解析格式
                    String dateFormat = environment.getProperty("spring.jackson.date-format");
                    String timeZone = environment.getProperty("spring.jackson.time-zone");

                    if (StringUtils.isBlank(dateFormat)) {
                        return;
                    }

                    for (int i = 0; i < list.size(); i++) {
                        if (list.get(i) instanceof MappingJackson2HttpMessageConverter) {
                            SimpleDateFormat simpleDateFormat= null;
                            if (StringUtils.isBlank(timeZone)) {
                                simpleDateFormat = new SimpleDateFormat(dateFormat);
                            } else {
                                simpleDateFormat = new SimpleDateFormat(dateFormat, new Locale(timeZone));
                            }
                            ((MappingJackson2HttpMessageConverter) list.get(i)).getObjectMapper().setDateFormat(simpleDateFormat);
                        }
                    }
                })
                .build();

        return restClient;
    }

    private ClientHttpRequestFactory getRequestFactory() {
        HttpClient httpClient = getHttpClient();

        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);

        factory.setConnectTimeout(5000); // 连接超时，单位为毫秒
        factory.setConnectionRequestTimeout(5000);    // 读取超时，单位为毫秒

        return factory;
    }

    private HttpClient getHttpClient() {
        // 创建连接池管理器
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(100); // 设置连接池的最大连接数
        connectionManager.setDefaultMaxPerRoute(50); // 每个路由的最大连接数

        // 配置 HttpClient
        return HttpClients.custom()
                .setConnectionManager(connectionManager)
                .evictIdleConnections(TimeValue.ofSeconds(30)) // 清理30秒内未使用的连接
                .build();
    }
}